package flexjson.factories;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import flexjson.JSONException;
import flexjson.ObjectBinder;
import flexjson.ObjectFactory;

public class DateObjectFactory implements ObjectFactory {
    List/*<DateFormat>*/ dateFormats;

    public DateObjectFactory() {
        dateFormats = new ArrayList/*<DateFormat>*/();
        dateFormats.add( DateFormat.getDateTimeInstance() );
        dateFormats.add(DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG ) );
        dateFormats.add(DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM ) );
        dateFormats.add(DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT ) );
        dateFormats.add( new SimpleDateFormat("EEE MMM d hh:mm:ss a z yyyy") );
        dateFormats.add( new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy") );
        dateFormats.add( new SimpleDateFormat("MM/dd/yy hh:mm:ss a"));
        dateFormats.add( new SimpleDateFormat("MM/dd/yy") );
    }

    public DateObjectFactory(List/*<DateFormat>*/ dateFormats) {
        this.dateFormats = dateFormats;
    }

    public Object instantiate(ObjectBinder context, Object value, Class targetType, Class targetClass) {
        try {
            if( value instanceof Double ) {
                return instantiateDate( (Class)targetType, new Long(((Double)value).longValue()), context );
            } else if( value instanceof Long ) {
                return instantiateDate( (Class)targetType, (Long)value, context );
            } else {
                for(int i = 0; i < dateFormats.size(); i ++) {
                	 DateFormat format = (DateFormat)dateFormats.get(i);
                    try {
                        return format.parse( value.toString() );
                    } catch (ParseException e) {
                        // try next format
                    }
                }
//                throw new JSONException( String.format("%s:  Parsing date %s was not recognized as a date format", context.getCurrentPath(), value ) );
                throw new JSONException("%s:  Parsing date %s was not recognized as a date format");
            }
        } catch (IllegalAccessException e) {
//            throw new JSONException( String.format("%s:  Error encountered trying to instantiate %s", context.getCurrentPath(), ((Class)targetType).getName() ), e);
        	throw new JSONException("%s:  Error encountered trying to instantiate %s", e);
        } catch (InstantiationException e) {
//            throw new JSONException( String.format("%s:  Error encountered trying to instantiate %s.  Make sure there is a public constructor that accepts a single Long.", context.getCurrentPath(), ((Class)targetType).getName() ), e);
        	throw new JSONException("%s:  Error encountered trying to instantiate %s.  Make sure there is a public constructor that accepts a single Long.", e);
        } catch (InvocationTargetException e) {
//            throw new JSONException( 
//            		String.format("%s:  Error encountered trying to instantiate %s.  Make sure there is a public constructor that accepts a single Long.", 
//            				context.getCurrentPath(), ((Class)targetType).getName() ), e);
        	throw new JSONException( "%s:  Error encountered trying to instantiate %s.  Make sure there is a public constructor that accepts a single Long.", e);
        }
    }

    private Date instantiateDate( Class targetType, Long value, ObjectBinder context ) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        try {
            Constructor constructor = targetType.getConstructor(new Class[] {Long.TYPE});
            return (Date)constructor.newInstance(new Object[]{ value });
        } catch (NoSuchMethodException e) {
            Date d = (Date)targetType.newInstance();
            d.setTime( value.longValue() );
            return d;
        }
    }
}
